﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Felbot.Interface.Users;
using Felbot.Profiles;

namespace Felbot.Interface.Conversations {
    /// <summary>This extended text box class allows for more keystroke recognition specific to this bot.</summary>
    public class TextBoxEx : TextBox {
        private frmProfile m_frmProfile;
        private LinkedList<string> m_RecentlySaid = new LinkedList<string>();

        internal void Initialize(frmProfile frmProfile) {
            m_frmProfile = frmProfile;
        }

        /// <summary>
        /// Gets a value indicating the state of the
        /// System.Windows.Forms.TextBoxBase.ShortcutsEnabled property.
        /// </summary>
        /// <param name="msg">
        /// A System.Windows.Forms.Message, passed by reference that
        /// represents the window message to process.
        /// </param>
        /// <param name="keyData">
        /// One of the System.Windows.Forms.Keys values that represents
        /// the shortcut key to process.
        /// </param>
        /// <returns>true if the shortcut key was processed by the control; otherwise, false.</returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
            if (keyData == Keys.Tab) {
                #region Auto-Completion
                #region Initialization
                string selected = this.SelectedText;
                string newText = this.Text.Substring(0, this.SelectionStart) + this.Text.Substring(this.SelectionStart + this.SelectionLength);
                int start = newText.Substring(0, this.SelectionStart).LastIndexOf(' ') + 1;
                int end = newText.IndexOf(' ', start);
                if (end == -1)
                    end = newText.Length;
                int len = end - start;
                string before = newText.Substring(0, start);
                string after = newText.Substring(end);
                string incomplete = newText.Substring(start, len);
                #endregion

                #region Populate Auto-Completion List
                List<string> autoCompleteList = new List<string>();
                if (ProfileControl.Global.GetSetting("Interface", "AutoCompleteChannel").ToBoolean(false)) {
                    foreach (ChannelUser m_lUser in m_frmProfile.Channel.Users) {
                        if (!autoCompleteList.Contains(m_lUser.Username))
                            autoCompleteList.Add(m_lUser.Username);
                    }
                }
                if (ProfileControl.Global.GetSetting("Interface", "AutoCompleteFriends").ToBoolean(false)) {
                    foreach (FriendUser user in m_frmProfile.Friends.Users) {
                        if (!autoCompleteList.Contains(user.Username))
                            autoCompleteList.Add(user.Username);
                    }
                }
                if (ProfileControl.Global.GetSetting("Interface", "AutoCompleteClan").ToBoolean(false)) {
                    foreach (ClanMember user in m_frmProfile.Clan.Users) {
                        if (!autoCompleteList.Contains(user.Username))
                            autoCompleteList.Add(user.Username);
                    }
                }
                #endregion

                #region Complete
                string complete = ""; string completeSelected = "";
                for (int i = 0; i < autoCompleteList.Count; i++) {
                    if (autoCompleteList[i].Equals(incomplete + selected, StringComparison.CurrentCultureIgnoreCase)) {
                        for (int j = i + 1; j <= autoCompleteList.Count; j++) {
                            if (j == autoCompleteList.Count) {
                                j = -1;
                                continue;
                            } else if (j == i) {
                                break;
                            }
                            if (autoCompleteList[j].StartsWith(incomplete, StringComparison.CurrentCultureIgnoreCase)) {
                                complete = autoCompleteList[j].Substring(0, incomplete.Length);
                                completeSelected = autoCompleteList[j].Substring(incomplete.Length);
                                break;
                            }
                        }
                    }
                    if (completeSelected != "")
                        break;
                }
                if (completeSelected == "") {
                    for (int i = 0; i < autoCompleteList.Count; i++) {
                        if (autoCompleteList[i].StartsWith(incomplete, StringComparison.CurrentCultureIgnoreCase)) {
                            complete = autoCompleteList[i].Substring(0, incomplete.Length);
                            completeSelected = autoCompleteList[i].Substring(incomplete.Length);
                            break;
                        }
                    }
                }

                if (completeSelected != "" || complete != "") {
                    this.Text = before + complete + completeSelected + after;
                    this.SelectionStart = before.Length + complete.Length;
                    this.SelectionLength = completeSelected.Length;
                }
                #endregion
                return true;
                #endregion
            } else if (keyData == Keys.Enter) {
                #region Send Text and add to Recent list
                m_frmProfile.PacketThread.SendText(this.Text);
                if (m_RecentlySaid.Contains(this.Text))
                    m_RecentlySaid.Remove(this.Text);
                if (ProfileControl.Global.GetSetting("Interface", "AutoCompleteRecent").ToBoolean(false) && this.Text != "")
                    m_RecentlySaid.AddFirst(this.Text);
                if (m_RecentlySaid.Count > ProfileControl.Global.GetSetting("Interface", "AutoCompleteRecentNumber").ToInteger(15))
                    m_RecentlySaid.RemoveLast();
                this.Text = "";
                return true;
                #endregion
            } else if (keyData == Keys.Space) {
                #region /r and /rw
                string reply;
                if (this.Text.Equals("/r", StringComparison.CurrentCultureIgnoreCase) ||
                    this.Text.Equals("/reply", StringComparison.CurrentCultureIgnoreCase)) {
                    reply = m_frmProfile.PacketThread.LastWhisperRecieved;
                    if (reply != null) {
                        this.Text = "/w " + reply + " ";
                        this.SelectionStart = this.Text.Length;
                        this.SelectionLength = 0;
                        return true;
                    }
                } else if (this.Text.Equals("/rw", StringComparison.CurrentCultureIgnoreCase) ||
                         this.Text.Equals("/rewhisper", StringComparison.CurrentCultureIgnoreCase) ||
                         this.Text.Equals("/remsg", StringComparison.CurrentCultureIgnoreCase) ||
                         this.Text.Equals("/remessage", StringComparison.CurrentCultureIgnoreCase)) {
                    reply = m_frmProfile.PacketThread.LastWhisperSent;
                    if (reply != null) {
                        this.Text = "/w " + reply + " ";
                        this.SelectionStart = this.Text.Length;
                        this.SelectionLength = 0;
                        return true;
                    }
                }
                #endregion
            } else if (keyData == (Keys.Control | Keys.X)) {
                #region Cut
                this.Cut();
                return true;
                #endregion
            } else if (keyData == (Keys.Control | Keys.C)) {
                #region Copy
                this.Copy();
                return true;
                #endregion
            } else if (keyData == (Keys.Control | Keys.V)) {
                #region Paste
                this.Paste();
                return true;
                #endregion
            } else if (keyData == Keys.Delete) {
                #region Delete
                this.Delete();
                return true;
                #endregion
            } else if (keyData == Keys.Down || keyData == Keys.PageDown || keyData == Keys.BrowserBack) {
                //scroll down recently said; last (newest) > first (oldest)
                #region Recently Said Down
                if (m_RecentlySaid.Count == 0)
                    return true;
                if (m_RecentlySaid.Contains(this.Text)) {
                    LinkedListNode<string> recentItemList = m_RecentlySaid.First;
                    while (recentItemList.Value != this.Text) {
                        recentItemList = recentItemList.Next;
                    }
                    if (recentItemList.Next == null)
                        this.Text = m_RecentlySaid.First.Value;
                    else
                        this.Text = recentItemList.Next.Value;
                } else
                    this.Text = m_RecentlySaid.First.Value;
                this.SelectionStart = this.Text.Length;
                this.SelectionLength = 0;
                return true;
                #endregion
            } else if (keyData == Keys.Up || keyData == Keys.PageUp || keyData == Keys.BrowserForward) {
                //scroll up recently said; first (oldest) > last (newest)
                #region Recently Said Up
                if (m_RecentlySaid.Count == 0)
                    return true;
                if (m_RecentlySaid.Contains(this.Text)) {
                    LinkedListNode<string> recentItemList = m_RecentlySaid.Last;
                    while (recentItemList.Value != this.Text) {
                        recentItemList = recentItemList.Previous;
                    }
                    if (recentItemList.Previous == null)
                        this.Text = m_RecentlySaid.Last.Value;
                    else
                        this.Text = recentItemList.Previous.Value;
                } else
                    this.Text = m_RecentlySaid.Last.Value;
                this.SelectionStart = this.Text.Length;
                this.SelectionLength = 0;
                return true;
                #endregion
            }

            return base.ProcessCmdKey(ref msg, keyData);
        }

        protected override bool ProcessDialogKey(Keys keyData) {
            return base.ProcessDialogKey(keyData);
        }

        /// <summary>Cuts the selected text out.</summary>
        public new void Cut() {
            if (this.SelectionLength > 0) {
                Clipboard.SetText(this.SelectedText);
                int start = this.SelectionStart;
                this.Text = this.Text.Remove(this.SelectionStart, this.SelectionLength);
                this.SelectionStart = start;
                this.SelectionLength = 0;
            }
        }

        /// <summary>Copies the selected text.</summary>
        public new void Copy() {
            if (this.SelectionLength > 0) {
                Clipboard.SetText(this.SelectedText);
            }
        }

        /// <summary>Pastes the stored text. Allows for multi-line pasting.</summary>
        public new void Paste() {
            if (Clipboard.ContainsText()) {
                // the following line takes the clipboard text and
                // makes sure that there is only Environment.Newline,
                // and not \r and \n separately, which isn't split
                string pasteText = Clipboard.GetText().Replace("\0", "").Replace('\r', '\0').Replace('\n', '\0').Replace("\0\0", "\0").Replace("\0", Environment.NewLine);
                if (pasteText.Contains(Environment.NewLine)) {
                    string textBefore = this.Text.Substring(0, this.SelectionStart);
                    string textAfter = this.Text.Substring(this.SelectionStart + this.SelectionLength);
                    string[] lines = pasteText.Split(new string[] { Environment.NewLine }, 256, StringSplitOptions.RemoveEmptyEntries);
                    if (textBefore.Length > 0)
                        lines[0] = textBefore + lines[0];
                    if (textAfter.Length > 0)
                        lines[lines.Length - 1] += textAfter;
                    for (int i = 0; i < lines.Length; i++) {
                        if (lines[i].Trim().Length > 0) {
                            m_frmProfile.PacketThread.SendText(lines[i]);
                        }
                    }
                    this.Text = "";
                } else {
                    int start = this.SelectionStart;
                    string textBefore = this.Text.Substring(0, this.SelectionStart);
                    string textAfter = this.Text.Substring(this.SelectionStart + this.SelectionLength);
                    this.Text = textBefore + pasteText + textAfter;
                    this.SelectionStart = start + pasteText.Length;
                    this.SelectionLength = 0;
                }
            }
        }

        /// <summary>Deletes the selected text or deletes the character after the carot.</summary>
        public void Delete() {
            int start = this.SelectionStart;
            if (this.SelectionLength > 0) {
                this.Text = this.Text.Remove(this.SelectionStart, this.SelectionLength);
            } else if (this.SelectionStart < this.Text.Length) {
                this.Text = this.Text.Remove(this.SelectionStart, 1);
            }
            this.SelectionStart = start;
            this.SelectionLength = 0;
        }
    }
}
